{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a href=\"https://githubtocolab.com/gee-community/geemap/blob/master/docs/notebooks/148_chart_data_table.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open in Colab\"/></a>\n",
    "\n",
    "**DataTable Charts**\n",
    "\n",
    "Uncomment the following line to install [geemap](https://geemap.org) if needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %pip install -U geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap\n",
    "from geemap import chart\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_initialize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Manual DataTable chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = {\n",
    "    \"State\": [\"CA\", \"NY\", \"IL\", \"MI\", \"OR\"],\n",
    "    \"Population\": [37253956, 19378102, 12830632, 9883640, 3831074],\n",
    "}\n",
    "\n",
    "df = pd.DataFrame(data)\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = chart.Chart(\n",
    "    df,\n",
    "    x_cols=[\"State\"],\n",
    "    y_cols=[\"Population\"],\n",
    "    chart_type=\"ColumnChart\",\n",
    "    colors=[\"#1d6b99\"],\n",
    "    title=\"State Population (US census, 2010)\",\n",
    "    x_label=\"State\",\n",
    "    y_label=\"Population\",\n",
    ")\n",
    "fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/vuxNmuh.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Computed DataTable chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the example feature collection and subset the forest feature.\n",
    "forest = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n",
    "    ee.Filter.eq(\"label\", \"Forest\")\n",
    ")\n",
    "\n",
    "# Load MODIS vegetation indices data and subset a decade of images.\n",
    "veg_indices = (\n",
    "    ee.ImageCollection(\"MODIS/061/MOD13A1\")\n",
    "    .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n",
    "    .select([\"NDVI\", \"EVI\"])\n",
    ")\n",
    "\n",
    "# Build a feature collection where each feature has a property that represents\n",
    "# a DataFrame row.\n",
    "\n",
    "\n",
    "def aggregate(img):\n",
    "    # Reduce the image to the mean of pixels intersecting the forest ecoregion.\n",
    "    stat = img.reduceRegion(\n",
    "        **{\"reducer\": ee.Reducer.mean(), \"geometry\": forest, \"scale\": 500}\n",
    "    )\n",
    "\n",
    "    # Extract the reduction results along with the image date.\n",
    "    date = geemap.image_date(img)\n",
    "    evi = stat.get(\"EVI\")\n",
    "    ndvi = stat.get(\"NDVI\")\n",
    "\n",
    "    # Make a list of observation attributes to define a row in the DataTable.\n",
    "    row = ee.List([date, evi, ndvi])\n",
    "\n",
    "    # Return the row as a property of an ee.Feature.\n",
    "    return ee.Feature(None, {\"row\": row})\n",
    "\n",
    "\n",
    "reduction_table = veg_indices.map(aggregate)\n",
    "\n",
    "# Aggregate the 'row' property from all features in the new feature collection\n",
    "# to make a server-side 2-D list (DataTable).\n",
    "data_table_server = reduction_table.aggregate_array(\"row\")\n",
    "\n",
    "# Define column names and properties for the DataTable. The order should\n",
    "# correspond to the order in the construction of the 'row' property above.\n",
    "column_header = ee.List([[\"Date\", \"EVI\", \"NDVI\"]])\n",
    "\n",
    "# Concatenate the column header to the table.\n",
    "data_table_server = column_header.cat(data_table_server)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_table = chart.DataTable(data_table_server, date_column=\"Date\")\n",
    "data_table.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = chart.Chart(\n",
    "    data_table,\n",
    "    chart_type=\"LineChart\",\n",
    "    x_cols=\"Date\",\n",
    "    y_cols=[\"EVI\", \"NDVI\"],\n",
    "    colors=[\"#e37d05\", \"#1d6b99\"],\n",
    "    title=\"Average Vegetation Index Value by Date for Forest\",\n",
    "    x_label=\"Date\",\n",
    "    y_label=\"Vegetation index (x1e4)\",\n",
    "    stroke_width=3,\n",
    "    legend_location=\"right\",\n",
    ")\n",
    "fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/PWei7QC.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interval chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define a point to extract an NDVI time series for.\n",
    "geometry = ee.Geometry.Point([-121.679, 36.479])\n",
    "\n",
    "# Define a band of interest (NDVI), import the MODIS vegetation index dataset,\n",
    "# and select the band.\n",
    "band = \"NDVI\"\n",
    "ndvi_col = ee.ImageCollection(\"MODIS/061/MOD13Q1\").select(band)\n",
    "\n",
    "# Map over the collection to add a day of year (doy) property to each image.\n",
    "\n",
    "\n",
    "def set_doy(img):\n",
    "    doy = ee.Date(img.get(\"system:time_start\")).getRelative(\"day\", \"year\")\n",
    "    # Add 8 to day of year number so that the doy label represents the middle of\n",
    "    # the 16-day MODIS NDVI composite.\n",
    "    return img.set(\"doy\", ee.Number(doy).add(8))\n",
    "\n",
    "\n",
    "ndvi_col = ndvi_col.map(set_doy)\n",
    "\n",
    "# Join all coincident day of year observations into a set of image collections.\n",
    "distinct_doy = ndvi_col.filterDate(\"2013-01-01\", \"2014-01-01\")\n",
    "filter = ee.Filter.equals(**{\"leftField\": \"doy\", \"rightField\": \"doy\"})\n",
    "join = ee.Join.saveAll(\"doy_matches\")\n",
    "join_col = ee.ImageCollection(join.apply(distinct_doy, ndvi_col, filter))\n",
    "\n",
    "# Calculate the absolute range, interquartile range, and median for the set\n",
    "# of images composing each coincident doy observation group. The result is\n",
    "# an image collection with an image representative per unique doy observation\n",
    "# with bands that describe the 0, 25, 50, 75, 100 percentiles for the set of\n",
    "# coincident doy images.\n",
    "\n",
    "\n",
    "def cal_percentiles(img):\n",
    "    doyCol = ee.ImageCollection.fromImages(img.get(\"doy_matches\"))\n",
    "\n",
    "    return doyCol.reduce(\n",
    "        ee.Reducer.percentile([0, 25, 50, 75, 100], [\"p0\", \"p25\", \"p50\", \"p75\", \"p100\"])\n",
    "    ).set({\"doy\": img.get(\"doy\")})\n",
    "\n",
    "\n",
    "comp = ee.ImageCollection(join_col.map(cal_percentiles))\n",
    "\n",
    "# Extract the inter-annual NDVI doy percentile statistics for the\n",
    "# point of interest per unique doy representative. The result is\n",
    "# is a feature collection where each feature is a doy representative that\n",
    "# contains a property (row) describing the respective inter-annual NDVI\n",
    "# variance, formatted as a list of values.\n",
    "\n",
    "\n",
    "def order_percentiles(img):\n",
    "    stats = ee.Dictionary(\n",
    "        img.reduceRegion(\n",
    "            **{\"reducer\": ee.Reducer.first(), \"geometry\": geometry, \"scale\": 250}\n",
    "        )\n",
    "    )\n",
    "\n",
    "    # Order the percentile reduction elements according to how you want columns\n",
    "    # in the DataTable arranged (x-axis values need to be first).\n",
    "    row = ee.List(\n",
    "        [\n",
    "            img.get(\"doy\"),\n",
    "            stats.get(band + \"_p50\"),\n",
    "            stats.get(band + \"_p0\"),\n",
    "            stats.get(band + \"_p25\"),\n",
    "            stats.get(band + \"_p75\"),\n",
    "            stats.get(band + \"_p100\"),\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    # Return the row as a property of an ee.Feature.\n",
    "    return ee.Feature(None, {\"row\": row})\n",
    "\n",
    "\n",
    "reduction_table = comp.map(order_percentiles)\n",
    "\n",
    "# Aggregate the 'row' properties to make a server-side 2-D array (DataTable).\n",
    "data_table_server = reduction_table.aggregate_array(\"row\")\n",
    "\n",
    "# Define column names and properties for the DataTable. The order should\n",
    "# correspond to the order in the construction of the 'row' property above.\n",
    "column_header = ee.List([[\"DOY\", \"median\", \"p0\", \"p25\", \"p75\", \"p100\"]])\n",
    "\n",
    "# Concatenate the column header to the table.\n",
    "data_table_server = column_header.cat(data_table_server)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = chart.DataTable(data_table_server)\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = chart.Chart(\n",
    "    df,\n",
    "    chart_type=\"IntervalChart\",\n",
    "    x_cols=\"DOY\",\n",
    "    y_cols=[\"p0\", \"p25\", \"median\", \"p75\", \"p100\"],\n",
    "    title=\"Annual NDVI Time Series with Inter-Annual Variance\",\n",
    "    x_label=\"Day of Year\",\n",
    "    y_label=\"Vegetation index (x1e4)\",\n",
    "    stroke_width=1,\n",
    "    fill=\"between\",\n",
    "    fill_colors=[\"#b6d1c6\", \"#83b191\", \"#83b191\", \"#b6d1c6\"],\n",
    "    fill_opacities=[0.6] * 4,\n",
    "    labels=[\"p0\", \"p25\", \"median\", \"p75\", \"p100\"],\n",
    "    display_legend=True,\n",
    "    legend_location=\"top-right\",\n",
    "    ylim=(0, 10000),\n",
    ")\n",
    "fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/i8ZrGPR.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "geo",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
